package com.chen.common.scheduler.config;

import com.chen.base.utils.AnnotationRouteUtil;
import com.chen.common.scheduler.annotation.Job;
import com.chen.common.scheduler.entity.JobTask;
import com.chen.common.scheduler.inter.JobTaskConfigInter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * 定时任务调度中心
 * @author: ~龙~
 * @DATE: 2023/10/27
 **/
@Slf4j
@Component
public class JobTaskConfig {

    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    @Resource
    private JobTaskConfigInter jobTaskConfigInter;
    /** 存放定时任务的Map, 用于停止定时任务 */
    private static final Map<String, ScheduledFuture<?>> FUTURE_MAP = new ConcurrentHashMap<>();
    /** 存放定时任务配置信息的Map */
    private static final Map<String, JobTask> TASK_CONFIG_MAP = new ConcurrentHashMap<>();

    /**
     * 容器启动后：
     *      延迟 initialDelay 毫秒后再执行一次定时器,
     *      以后每隔 fixedDelay 毫秒再执行一次该定时器。
     */
    @Scheduled(initialDelay = 5000, fixedDelay = 300000)
    public void refresh(){
        List<JobTask> jobTasks = jobTaskConfigInter.getAll();
        for (JobTask jobTask : jobTasks) {

            JobTask jt = TASK_CONFIG_MAP.get(jobTask.getTaskCode());
            if (jt != null){
                if ( !jobTask.toString().equals(jt.toString()) ){
                    log.info("定时任务配置变更 -> {}", jobTask);
                    taskJoin(jobTask);
                }
            }else {
                taskJoin(jobTask);
            }
        }
        // 解决无效定时任务配置问题
        for (String key : TASK_CONFIG_MAP.keySet()) {
            long count = jobTasks.stream().filter(t -> t.getTaskCode().equals(key)).count();
            if (count == 0){
                TASK_CONFIG_MAP.remove(key);
            }
        }
    }

    /**
     * 任务入队
     */
    private void taskJoin(JobTask task){
        try {
            switch (task.getOnOff()){
                case "on":
                    addTask(task.getTaskCode());
                    break;
                case "off":
                    cancelTask(task.getTaskCode());
                    break;
                default:
            }
        }catch (Exception e){
            task.setOnOff("off");
            log.error("", e);
        }

        TASK_CONFIG_MAP.put(task.getTaskCode(), task);
    }

    /**
     * 增加任务
     */
    public void addTask(String taskCode) throws Exception {
        AnnotationRouteUtil.route(Job.class, Job::value, taskCode, (bean, method, job) -> {
            cancelTask(taskCode);

            // 添加定时任务
            ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(() -> {
                try {
                    method.invoke(bean, new Object[method.getParameterCount()]);
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
            }, triggerContext -> {
                JobTask tc = jobTaskConfigInter.getTcByCode(taskCode);
                if (tc == null || "off".equals(tc.getOnOff())){
                    cancelTask(taskCode);
                    return null;
                }

                return new CronTrigger(tc.getCron()).nextExecutionTime(triggerContext);
            });

            FUTURE_MAP.put(taskCode,future);
        });
    }

    /**
     * 取消任务
     */
    public void cancelTask(String taskCode) {
        if (FUTURE_MAP.containsKey(taskCode)) {
            FUTURE_MAP.get(taskCode).cancel(false);
            FUTURE_MAP.remove(taskCode);
        }
    }

    /**
     * 执行一次
     */
    public void executeOnce(String taskCode) throws Exception {
        AnnotationRouteUtil.route(Job.class, Job::value, taskCode,
                (bean, method, job) -> method.invoke(bean, new Object[method.getParameterCount()])
        );
    }

    /**
     * 获取所有配置信息
     */
    public List<JobTask> getAllTaskConfig(){
        return new ArrayList<>(TASK_CONFIG_MAP.values());
    }

    @PreDestroy
    public void ruin(){
        log.info("JOB -> 关闭所有定时任务");
        FUTURE_MAP.keySet().forEach(this::cancelTask);
        TASK_CONFIG_MAP.clear();
    }
}
